/** @file    AudioBox.cpp
 *  @author  Alessandro Polo
 *  @version $Id: AudioBox.cpp 2829 2010-08-02 20:45:47Z alex $
 *  @brief
 * File containing methods for the wosh::building::AudioBox class.
 * The header for this class can be found in AudioBox.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/building/AudioBox.h>

 #include <core/ObjectAllocator.h>
 #include <core/List.h>
 #include <core/MethodsCommon.h>


namespace wosh {
 namespace building {

 WOSH_REGISTER(wosh::building::AudioBox, "wosh::building::BuildingObject", _AudioBox_VERSION, _static_AudioBox )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

AudioBox::AudioBox()
	: BuildingObject() {
	BuildingObject::setName( "wosh.building.AudioBox", _GET_Abs(Utilities::randomLong()) );
 }

void AudioBox::init() {
	Log(LOG_DEBUG, " Setting default properties and permissions.." );

	Properties.set( _AudioBox_KEY_Device, "", Permission(Permission::RW, Permission::RW, Permission::Read) );
//	Properties.set( _AudioBox_KEY_ExternalAudioBox, false, Permission(Permission::RW, Permission::RW, Permission::Read) );
//	Properties.set( _AudioBox_KEY_PowerWatt, false, Permission(Permission::RW, Permission::RW, Permission::Read) );

	this->state = AudioBox::AUDIOBOX_UNKNOWN;
	setState(AudioBox::AUDIOBOX_UNKNOWN, false);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodMessageResponse* mmPlay = Methods.create<MethodMessageResponse>( _AudioBox_METHOD_play, "play resource", Permission::R_XR_XR__() );
	mmPlay->setMethod( this, (MethodMessageResponsePtr)&AudioBox::mmDoPlay );

	MethodMessageResponse* mmEnqueue = Methods.create<MethodMessageResponse>( _AudioBox_METHOD_enqueue, "enqueue resource", Permission::R_XR_XR__() );
	mmEnqueue->setMethod( this, (MethodMessageResponsePtr)&AudioBox::mmDoEnqueue );

	MethodMessageResponse* mmStop = Methods.create<MethodMessageResponse>( _AudioBox_METHOD_stop, "stop playing", Permission::R_XR_XR__() );
	mmStop->setMethod( this, (MethodMessageResponsePtr)&AudioBox::mmDoStop );

	MethodMessageResponse* mmSynch = Methods.create<MethodMessageResponse>( _AudioBox_METHOD_synch, "synch with another player", Permission::R_XR_XR__() );
	mmSynch->setMethod( this, (MethodMessageResponsePtr)&AudioBox::mmDoSynch );

	MethodMessageResponse* mmMute = Methods.create<MethodMessageResponse>( _AudioBox_METHOD_mute, "mute on/off", Permission::R_XR_XR__() );
	mmMute->setMethod( this, (MethodMessageResponsePtr)&AudioBox::mmDoMute );

	MethodMessageResponse* mmSetVolume = Methods.create<MethodMessageResponse>( _AudioBox_METHOD_volume, "set volume", Permission::R_XR_XR__() );
	mmSetVolume->setMethod( this, (MethodMessageResponsePtr)&AudioBox::mmDoSetVolume );

	MethodWRESULTwSecurityToken* mmSetVolumeUp = Methods.create<MethodWRESULTwSecurityToken>( _AudioBox_METHOD_volume_up, "set volume up (+5%)", Permission::R_XR_XR__() );
	mmSetVolumeUp->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&AudioBox::mmDoSetVolumeUp );

	MethodWRESULTwSecurityToken* mmSetVolumeDown = Methods.create<MethodWRESULTwSecurityToken>( _AudioBox_METHOD_volume_down, "set volume down (-5%)", Permission::R_XR_XR__() );
	mmSetVolumeDown->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&AudioBox::mmDoSetVolumeDown );

	BuildingObject::init();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AudioBox::setChild( const std::string& object_uri, InterfacesProvider* interfaces ) {
	WRESULT ret = BuildingObject::setChild(object_uri, interfaces);
	std::string uri = getChildByInterface( _PlayerAudio_TYPE );
	Properties.transactionBeginWrite();
	Properties.update( _AudioBox_KEY_Device, uri );
	Properties.transactionEnd();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES

void AudioBox::busMessageChild( const Message& childMessage ) {
	if ( childMessage.isEmpty() ) return;
	if ( !childMessage.getContent()->isFact() ) return;
	const Fact* fact = childMessage.getContent()->asFact();
	if ( fact == NULL ) return;

	if ( fact->getEvent() == _Player_KEY_State ) {
		if ( !fact->hasData() ) {
			Log(LOG_WARNING, ":busMessageChild() EVENT 'PlayerState' Invalid, no Content!" );
			return;
		 }
		const Variant* data = NULL;
		if ( fact->getData()->isKindOf<Variant>() )
			data = dynamic_cast<const Variant*>( fact->getData() );
		if ( data == NULL ) {
			Log(LOG_WARNING, ":busMessageChild() EVENT 'PlayerState' Invalid, Not Data-Content!" );
			return;
		 }
		std::string value = data->toString();
		Log(LOG_INFO, ":busMessageChild() EVENT 'PlayerState' => %s [%s]", value.c_str(), data->getTypeAsString() );

//Properties.update( _AudioBox_KEY_volume, data->toInteger() );
		if ( value == "Playing" )
			setState(AudioBox::AUDIOBOX_PLAYING, true, childMessage.getSecurityToken());
		else if ( value == "Idle" )
			setState(AudioBox::AUDIOBOX_IDLE, true, childMessage.getSecurityToken());
		else
			setState(AudioBox::AUDIOBOX_UNKNOWN, true, childMessage.getSecurityToken());
	 }
	else
		Log(LOG_WARNING, ":busMessageChild() UNKNOWN EVENT '%s'", fact->getEvent().c_str() );
 }

////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS

void AudioBox::setState( AudioBox::AUDIOBOX_STATE new_state, bool raise_event, const SecurityToken* source_token ) {
	Properties.transactionBeginWrite();
	if ( this->state != new_state ) {
		this->state = new_state;
		Properties.update( _BuildingObject_KEY_State, getStateAsString() );
	 }
	Properties.update( _BuildingObject_KEY_StateTs, Variant(Utilities::std_time(), Variant::DATETIME) );
	Properties.transactionEnd();
	if ( this->raiseAlwaysEvents || raise_event ) {
		Log(LOG_VERBOSE, ":setState(%s) Raising Event.. ", getStateAsString() );
		BuildingObject::raiseEvent( new AudioBox_StateChanged(new_state), source_token );
	 }
	else
		Log(LOG_DEBUG, ":setState(%s) Not Raising Event.", getStateAsString() );
 }

//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS

WRESULT AudioBox::play( const std::string& resource, const SecurityToken* security ) {
	Request* request = new Request( _Player_METHOD_play, new Variant(resource) );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), request, security );
	return WRET_OK;
 }

WRESULT AudioBox::enqueue( const std::string& resource, const SecurityToken* security ) {
	Request* request = new Request( _Player_METHOD_enqueue, new Variant(resource) );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), request, security );
	return WRET_OK;
 }

WRESULT AudioBox::stop( const SecurityToken* security ) {
	Request* request = new Request( _Player_METHOD_stop );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), request, security );
	return WRET_OK;
 }

WRESULT AudioBox::synch( const std::string& target_audiobox, const SecurityToken* security ) {
	List* dList = new List(2);
	dList->push_back<Variant>( 0 );
	dList->push_back<Variant>( getChildByInterface(_PlayerAudio_TYPE) );
	Request* request = new Request( _Player_METHOD_synch, dList );
	Message* message = new Message(request);
	message->setSource(this);
	message->getDestination().fromString(target_audiobox);
	message->setDestinationBus(_Bus_Building);
	message->setSecurityToken(security->clone());
	BusBuilding.postMessage(message);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AudioBox::setVolume( int volume, const SecurityToken* security ) {
	Request* request =  new Request( _Player_METHOD_set_volume, new Variant(volume) );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), request, security );
	return WRET_OK;
 }

WRESULT AudioBox::setMuted( bool muted, const SecurityToken* security ) {
	std::string muteonoff = "off";
	if ( muted )
		muteonoff = "on";
	Request* request =  new Request( _Player_METHOD_set_mute, new Variant(muted) );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* AudioBox::mmDoPlay( const Message* messageRequest ) {
	if ( messageRequest == NULL || messageRequest->isEmpty() || !messageRequest->getContent()->isRequest() ) return NULL;
	const Request* request = messageRequest->getContent()->asRequest();
	std::string resStr = request->getArgument().toString();
	Log(LOG_INFO, ":mmDoPlay(%s)", resStr.c_str() );

	WRESULT ret = play(resStr, messageRequest->getSecurityToken() );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* AudioBox::mmDoEnqueue( const Message* messageRequest ) {
	if ( messageRequest == NULL || messageRequest->isEmpty() || !messageRequest->getContent()->isRequest() ) return NULL;
	const Request* request = messageRequest->getContent()->asRequest();
	std::string resStr = request->getArgument().toString();
	Log(LOG_INFO, ":mmDoEnqueue(%s)", resStr.c_str() );

	WRESULT ret = enqueue(resStr, messageRequest->getSecurityToken() );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* AudioBox::mmDoStop( const Message* messageRequest ) {
	if ( messageRequest == NULL || messageRequest->isEmpty() || !messageRequest->getContent()->isRequest() ) return NULL;
	const Request* request = messageRequest->getContent()->asRequest();
	Log(LOG_INFO, ":mmDoStop()" );

	WRESULT ret = stop( messageRequest->getSecurityToken() );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* AudioBox::mmDoSynch( const Message* messageRequest ) {
	if ( messageRequest == NULL || messageRequest->isEmpty() || !messageRequest->getContent()->isRequest() ) return NULL;
	const Request* request = messageRequest->getContent()->asRequest();
	WRESULT ret = WRET_ERR_INTERNAL;
	if ( request->getData()->isKindOf<Variant>() ) {
		std::string synchWith = request->getArgument().toString();
		Log(LOG_INFO, ":mmDoSynch() with %s", synchWith.c_str() );
		ret = synch( synchWith, messageRequest->getSecurityToken() );
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		// BUGGY BUG DEPRECATED
	 }
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* AudioBox::mmDoMute( const Message* messageRequest ) {
	if ( messageRequest == NULL || messageRequest->isEmpty() || !messageRequest->getContent()->isRequest() ) return NULL;
	const Request* request = messageRequest->getContent()->asRequest();
	std::string muteStr = request->getArgument().toString();
	Log(LOG_INFO, ":mmDoMute(%s)", muteStr.c_str() );

	WRESULT ret = WRET_ERR_PARAM;
	if ( muteStr == "on" )
		ret = setMuted( true, messageRequest->getSecurityToken() );
	else if ( muteStr == "off" )
		ret = setMuted( false, messageRequest->getSecurityToken() );

	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* AudioBox::mmDoSetVolume( const Message* messageRequest ) {
	if ( messageRequest == NULL || messageRequest->isEmpty() || !messageRequest->getContent()->isRequest() ) return NULL;
	const Request* request = messageRequest->getContent()->asRequest();
	std::string volumeStr = request->getArgument().toString();
	Log(LOG_INFO, ":mmDoSetVolume(%s)", volumeStr.c_str() );

	WRESULT ret = setVolume( Utilities::fromString<int>(volumeStr), messageRequest->getSecurityToken() );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AudioBox::mmDoSetVolumeUp( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoSetVolumeUp()" );
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), new Request( _Player_METHOD_set_volume, new Variant("+") ), security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT AudioBox::mmDoSetVolumeDown( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoSetVolumeDown()" );
	BuildingObject::controlChild( getChildByInterface(_PlayerAudio_TYPE), new Request( _Player_METHOD_set_volume, new Variant("-") ), security );
	return WRET_OK;
 }

////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool AudioBox::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = BuildingObject::updateHash( hash_context );
	ret = hash_context->update( this->state ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* AudioBoxStateStr[] = {
	"UNKNOWN",	// (AUDIOBOX_STATE)0
	"IDLE",		// (AUDIOBOX_STATE)1
	"PLAYING"	// (AUDIOBOX_STATE)2
 };

Enum2StrFromArray(AudioBox::getStateAsString, AudioBox::AUDIOBOX_STATE, AUDIOBOX_IDLE, AUDIOBOX_PLAYING, 0, AudioBoxStateStr)

Str2EnumByArray(AudioBox::getStateFromString, AudioBox::AUDIOBOX_STATE, 0, AUDIOBOX_PLAYING, AUDIOBOX_UNKNOWN, AudioBoxStateStr)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

AudioBox::AUDIOBOX_STATE intToAudioBoxStateEnum( int value ) {
	if ( value <= AudioBox::AUDIOBOX_UNKNOWN ) return AudioBox::AUDIOBOX_UNKNOWN;
	if ( value > AudioBox::AUDIOBOX_PLAYING ) return AudioBox::AUDIOBOX_UNKNOWN;
	return (AudioBox::AUDIOBOX_STATE)value;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::building::AudioBox_StateChanged, "wosh::Fact", 1.0, _static_AudioBox_StateChanged )

AudioBox_StateChanged::AudioBox_StateChanged( AudioBox::AUDIOBOX_STATE current_audiobox_state )
	: wosh::Fact( _BuildingObject_EVENT_StateChanged ) {
	setData( new Variant( (int)current_audiobox_state ) );
 }

AudioBox_StateChanged::AudioBox_StateChanged( AudioBox::AUDIOBOX_STATE current_audiobox_state, int current_volume )
	: wosh::Fact( _BuildingObject_EVENT_StateChanged ) {
	List* args = List::createFrom<Variant>( (int)current_audiobox_state, (int)current_volume );
	setData(args);
 }

AudioBox_StateChanged::AudioBox_StateChanged( AudioBox::AUDIOBOX_STATE current_audiobox_state, int current_volume,
												AudioBox::AUDIOBOX_STATE previous_audiobox_state, int previous_volume )
	: wosh::Fact( _BuildingObject_EVENT_StateChanged ) {
	List* list = new List(4);
	list->push_back<Variant>( (int)current_audiobox_state );
	list->push_back<Variant>( (int)current_volume );
	list->push_back<Variant>( (int)previous_audiobox_state );
	list->push_back<Variant>( (int)previous_volume );
	setData(list);
 }

AudioBox::AUDIOBOX_STATE AudioBox_StateChanged::getAudioBoxState() const {
	const Variant* var = getDataItem<Variant>(0);
	if ( var == NULL ) return AudioBox::AUDIOBOX_UNKNOWN;
	if ( var->isNumeric() ) return AudioBox::AUDIOBOX_UNKNOWN;
	return intToAudioBoxStateEnum(var->toInteger( (int)AudioBox::AUDIOBOX_UNKNOWN ));
 }

int AudioBox_StateChanged::getVolume() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return -1;
	if ( var->isNumeric() ) return -1;
	return var->toInteger(-1);
 }

AudioBox::AUDIOBOX_STATE AudioBox_StateChanged::getPreviousAudioBoxState() const {
	const Variant* var = getDataItem<Variant>(2);
	if ( var == NULL ) return AudioBox::AUDIOBOX_UNKNOWN;
	if ( var->isNumeric() ) return AudioBox::AUDIOBOX_UNKNOWN;
	return intToAudioBoxStateEnum(var->toInteger( (int)AudioBox::AUDIOBOX_UNKNOWN ));
 }

int AudioBox_StateChanged::getPreviousVolume() const {
	const Variant* var = getDataItem<Variant>(3);
	if ( var == NULL ) return -1;
	if ( var->isNumeric() ) return -1;
	return var->toInteger(-1);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace building
}; // namespace wosh
